using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;

namespace Microsoft.Coding4Fun.TweetCraft.Tray.Support
{
	/// <summary>
	/// Provides support for compress/decompress files using SharpZipLib.
	/// </summary>
	/// <remarks>
	/// <see cref="System.IO.Compression.GZipStream"/> does not support compressing/decompressing multiple files.
	/// </remarks>
	public static class Zipper
	{
		/// <summary>
		/// Compresses the specified files.
		/// </summary>
		/// <param name="fileNames">The file names.</param>
		/// <param name="zipFilePath">The zip file path.</param>
		public static void Compress(IEnumerable<string> fileNames, string zipFilePath)
		{
			if (fileNames == null)
				throw new ArgumentNullException("fileNames");
			if (zipFilePath == null)
				throw new ArgumentNullException("zipFilePath");

			using (var zipFileStream = new FileStream(zipFilePath, FileMode.Create, FileAccess.Write, FileShare.None))
			{
				var zipOutputStream = new ZipOutputStream(zipFileStream);
				// Zip with highest compression.
				zipOutputStream.SetLevel(9);

				foreach (var fileName in fileNames)
				{
					if (!File.Exists(fileName))
						throw new FileNotFoundException();

					using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
					{
						// Read full stream to in-memory buffer.
						var buffer = new byte[fileStream.Length];
						fileStream.Read(buffer, 0, buffer.Length);
						// Create a new entry for the current file.
						var entry = new ZipEntry(Path.GetFileName(fileName))
						            	{
						            		DateTime = DateTime.Now,
						            		Size = fileStream.Length
						            	};
						fileStream.Close();
						// Update entry and write to zip stream.
						zipOutputStream.PutNextEntry(entry);
						zipOutputStream.Write(buffer, 0, buffer.Length);
					}
				}
				zipOutputStream.Finish();
				// Flushes the create and close.
				zipOutputStream.Flush();
				zipOutputStream.Close();
			}
		}

		/// <summary>
		/// Uncompress the given zip file.
		/// </summary>
		/// <param name="zipFilePath">The zip file path.</param>
		/// <param name="destinationFolder">The destination folder.</param>
		public static void Uncompress(string zipFilePath, string destinationFolder)
		{
			if (zipFilePath == null)
				throw new ArgumentNullException("zipFilePath");
			if (destinationFolder == null)
				throw new ArgumentNullException("destinationFolder");


			ZipEntry theEntry;
			var zipInputStream = new ZipInputStream(File.OpenRead(zipFilePath));

			while ((theEntry = zipInputStream.GetNextEntry()) != null)
			{
				string directoryName = Path.GetDirectoryName(theEntry.Name);
				string fileName = Path.GetFileName(theEntry.Name);
				//Create directory
				if (directoryName.Length > 0)
					Directory.CreateDirectory(Path.Combine(destinationFolder, directoryName));

				if (!String.IsNullOrEmpty(fileName))
				{
					FileStream streamWriter = File.Create((Path.Combine(destinationFolder, theEntry.Name)));
					var data = new byte[2048];
					while (true)
					{
						int size = zipInputStream.Read(data, 0, data.Length);

						if (size > 0)
							streamWriter.Write(data, 0, size);
						else
							break;
					}

					streamWriter.Close();
				}
			}

			zipInputStream.Close();
		}
	}
}